package com.ihr360.payroll.service.salarytasklet.step.processor;

import com.ihr360.commons.data.util.Pair;
import com.ihr360.expr.engine.DependencyEngine;
import com.ihr360.job.core.item.ItemProcessor;
import com.ihr360.job.core.repository.JobRepository;
import com.ihr360.payperiod.PayPeriodTerm;
import com.ihr360.payroll.dao.MergeSalaryReportRepository;
import com.ihr360.payroll.dao.MergeSalaryReportTermSalaryRepository;
import com.ihr360.payroll.dao.PayPeriodTermRepository;
import com.ihr360.payroll.model.dto.salary.meta.PayrollConfig;
import com.ihr360.payroll.model.dto.salary.meta.SalaryField;
import com.ihr360.payroll.model.dto.salary.plan.SalaryPlan;
import com.ihr360.payroll.model.dto.salary.report.HistoryPayPeriod;
import com.ihr360.payroll.model.dto.salary.report.HistorySalaryPlan;
import com.ihr360.payroll.model.dto.salary.report.MergeSalaryReport;
import com.ihr360.payroll.model.dto.salary.report.MergeSalaryReportTermSalary;
import com.ihr360.payroll.model.dto.salary.report.embedded.MergeSalaryField;
import com.ihr360.payroll.model.dto.salary.report.embedded.MergeStaffList;
import com.ihr360.payroll.model.dto.salary.report.embedded.PayrollConfigSnapshot;
import com.ihr360.payroll.model.dto.salary.report.embedded.SalaryFieldSnapshot;
import com.ihr360.payroll.model.dto.salary.task.period.ActivePayPeriod;
import com.ihr360.payroll.model.response.salaryreport.SalaryHeader;
import com.ihr360.payroll.model.vo.SalaryFieldEntity;
import com.ihr360.payroll.service.salaryreport.impl.HistorySalaryPlanServiceImpl;
import com.ihr360.payroll.service.salarytasklet.step.SalaryCalcContext;
import com.ihr360.payroll.type.ReportState;
import com.ihr360.shared.basicstaffinfo.dto.SyncedBasicStaffInfo;
import com.ihr360.validation.ValidationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author davidwei
 */
@Component
public class MergeTaxSalaryReportMetaProcessor implements ItemProcessor<SalaryCalcContext, SalaryCalcContext> {
    final Logger logger = LoggerFactory.getLogger(DependencyEngine.class.getName());

    @Autowired
    private JobRepository jobRepository;

    @Autowired
    private PayPeriodTermRepository payPeriodTermRepository;

    @Autowired
    private MergeSalaryReportRepository mergeSalaryReportRepository;

    @Autowired
    private MergeSalaryReportTermSalaryRepository mergeSalaryReportTermSalaryRepository;

    /**
     * @param salaryCalcContext
     * @return
     * @throws Exception
     */
    @Override
    public SalaryCalcContext process(SalaryCalcContext salaryCalcContext) throws Exception {
        PayPeriodTerm payPeriodTerm = salaryCalcContext.payPeriodTerm;
        Long payPeriodTermId = payPeriodTerm.getId();
        Long basedOnTermId = payPeriodTerm.getBasedOnTermId();
        ValidationUtils.rejectIfEmpty(payPeriodTermId, null, "账期标识不能为空");
        ValidationUtils.rejectIfEmpty(basedOnTermId, null, "合并计税基准账期标识不能为空");
        Long mergeSalaryReportId = payPeriodTerm.getMergeSalaryReportId();
        Long mergeSalaryReportTermSalaryId = payPeriodTerm.getMergeSalaryReportTermSalaryId();

        com.ihr360.payroll.model.dto.salary.task.period.PayPeriodTerm periodTerm = payPeriodTermRepository.findOne(payPeriodTermId);
        com.ihr360.payroll.model.dto.salary.task.period.PayPeriodTerm basedOnTerm = payPeriodTermRepository.findOne(basedOnTermId);
        ValidationUtils.rejectIfEmpty(periodTerm, null, "无法获取账期信息");
        ValidationUtils.rejectIfEmpty(basedOnTerm, null, "无法获取合并计税账期信息");

        ActivePayPeriod payPeriod = periodTerm.getPayPeriod();
        SalaryPlan salaryPlan = payPeriod.getSalaryPlan();
        PayrollConfig payrollConfig = salaryPlan.getPayrollConfig();
        List<SalaryField> fields = payrollConfig.getFields();

        HistoryPayPeriod mergePayPeriod = basedOnTerm.getHistoryPayPeriod();

        ValidationUtils.rejectIfEmpty(mergePayPeriod, null, "无法获取合并计税账期信息");

        HistorySalaryPlan mergeSalaryPlan = mergePayPeriod.getSalaryPlan();


        PayrollConfigSnapshot mergePayrollConfig = mergeSalaryPlan.getPayrollConfig();
        List<SalaryFieldSnapshot> mergeTermfields = mergePayrollConfig.getFields();


        Set<String> fieldsCodesSet = new HashSet<>();
        //构建表头

        Pair<MergeSalaryField, SalaryHeader> headerPair = buildHeader(mergeTermfields, fields, fieldsCodesSet);
        MergeSalaryReport mergeSalaryReport = buildMergeSalaryReport(salaryCalcContext, mergeSalaryReportId, headerPair.getFirst());
        mergeSalaryReport.setName(String.format("%s%s(%s-%s)", payPeriodTerm.getPlanName(), "汇总表", payPeriodTerm.getYear(), payPeriodTerm.getMonth()));
        mergeSalaryReportRepository.save(mergeSalaryReport);
        mergeSalaryReportId = mergeSalaryReport.getId();
        payPeriodTerm.setMergeSalaryReportId(mergeSalaryReportId);
        periodTerm.setMergeSalaryReportId(mergeSalaryReportId);

        MergeSalaryReportTermSalary reportTermSalary = buildMergeSalaryReportTermSalary(mergeSalaryReportTermSalaryId, mergeSalaryReportId, payPeriodTerm.getStartOn(), headerPair.getSecond());
        reportTermSalary.setMergeReportId(mergeSalaryReportId);
        mergeSalaryReportTermSalaryRepository.save(reportTermSalary);
        mergeSalaryReportTermSalaryId = reportTermSalary.getId();
        payPeriodTerm.setMergeSalaryReportTermSalaryId(mergeSalaryReportTermSalaryId);
        periodTerm.setMergeSalaryReportTermSalaryId(mergeSalaryReportTermSalaryId);
        return salaryCalcContext;
    }


    private MergeSalaryReportTermSalary buildMergeSalaryReportTermSalary(Long mergeSalaryReportTermSalaryId, Long mergeSalaryReportId, Date termDate, SalaryHeader header) {
        MergeSalaryReportTermSalary salaryReportTermSalary = null;
        if (mergeSalaryReportTermSalaryId == null) {
            salaryReportTermSalary = new MergeSalaryReportTermSalary(mergeSalaryReportId, termDate, 1);
        } else {
            salaryReportTermSalary = mergeSalaryReportTermSalaryRepository.findOne(mergeSalaryReportTermSalaryId);
            if (salaryReportTermSalary == null) {
                salaryReportTermSalary = new MergeSalaryReportTermSalary(mergeSalaryReportId, termDate, 1);
            }
        }
        salaryReportTermSalary.setReportState(ReportState.STARTED);
        salaryReportTermSalary.setSalaryHeader(header);
        return salaryReportTermSalary;
    }


    private MergeSalaryReport buildMergeSalaryReport(SalaryCalcContext salaryCalcContext, Long mergeSalaryReportId, MergeSalaryField header) {
        MergeSalaryReport report = null;
        //如果为空认为是首次创建
        if (mergeSalaryReportId == null) {
            report = getDefaultMergeSalaryReport(salaryCalcContext);
        } else {
            report = mergeSalaryReportRepository.findOne(mergeSalaryReportId);
            if (report == null) {
                report = getDefaultMergeSalaryReport(salaryCalcContext);
            }
        }

        List<? extends String> staffIds = salaryCalcContext.salaryPlanStaffListItemWriter.getWrittenItems();
        List<String> staffIdList = staffIds.stream().map(s -> s.toString()).collect(Collectors.toList());
        report.setMergeSalaryField(header);
        MergeStaffList mergeStaffList = new MergeStaffList();
        mergeStaffList.setStaffIds(staffIdList);
        return report;
    }

    private MergeSalaryReport getDefaultMergeSalaryReport(SalaryCalcContext salaryCalcContext) {
        MergeSalaryReport report;
        report = new MergeSalaryReport();
        report.setRemark("合并计税合并报表");
        report.setJoinType(MergeSalaryReport.JoinType.CROSS_JOIN);
        //Cross情况，那个哪个是基准其实是没有关系的。
        report.setJoinSalaryPlanId(salaryCalcContext.salaryPlanId);
        report.setIsMergeTax(true);
        return report;
    }

    /**
     * 构建表头
     *
     * @param mergeTaxTermfields 合并计税合并表字段
     * @param fields             合并计税基本表项目
     * @return
     */
    private Pair<MergeSalaryField, SalaryHeader> buildHeader(List<SalaryFieldSnapshot> mergeTaxTermfields, List<SalaryField> fields, Set<String> fieldsCodesSet) {
        List<SalaryFieldSnapshot> snapshots = new ArrayList<>(mergeTaxTermfields.size() + fields.size());
        mergeTaxTermfields.stream().forEach(f -> {
                    fieldsCodesSet.add(f.getCode());
                    SalaryFieldSnapshot snapshot = new SalaryFieldSnapshot();
                    BeanUtils.copyProperties(f, snapshot);
                    snapshots.add(snapshot);
                }
        );
        fields.stream().forEach(f -> {
            if (!fieldsCodesSet.contains(f.getCode())) {
                fieldsCodesSet.add(f.getCode());
                SalaryFieldSnapshot snapshot = new SalaryFieldSnapshot();
                BeanUtils.copyProperties(f, snapshot);
                snapshots.add(snapshot);
            }
        });
        snapshots.sort((s1, s2) -> s1.getIndexno() - s1.getIndexno());
        MergeSalaryField mergeSalaryField = new MergeSalaryField();
        mergeSalaryField.setFields(snapshots);

        Map<String, SalaryFieldEntity> headMap = new LinkedHashMap<>(snapshots.size() + 3);

        headMap.put(HistorySalaryPlanServiceImpl.EXCEL_FIELD_NAME, new SalaryFieldEntity(HistorySalaryPlanServiceImpl.EXCEL_FIELD_NAME, SyncedBasicStaffInfo.Field.staffName, true));
        headMap.put(HistorySalaryPlanServiceImpl.DEPARTMENT_NAME, new SalaryFieldEntity(HistorySalaryPlanServiceImpl.DEPARTMENT_NAME, SyncedBasicStaffInfo.Field.departmentName, true));
        headMap.put(HistorySalaryPlanServiceImpl.EXCEL_FIELD_MOBILE_NO, new SalaryFieldEntity(HistorySalaryPlanServiceImpl.EXCEL_FIELD_MOBILE_NO, SyncedBasicStaffInfo.Field.mobileNo, true));

        snapshots.stream().forEach(s -> {
                    SalaryFieldEntity entity = new SalaryFieldEntity(s.getName(), s.getCode(), true);
                    entity.setSource(s.getSource());
                    entity.setFormula(s.getFormula());
                    headMap.put(s.getName(), entity);
                }
        );
        SalaryHeader salaryHeader = new SalaryHeader();
        salaryHeader.setHeadMap(headMap);
        return Pair.of(mergeSalaryField, salaryHeader);
    }

}